﻿using System;
using System.Collections.Generic;
using System.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Security;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Creates a forum.
    /// </summary>
    public class CreateForum : CreateAuditable<ForumEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FORUM");
            }
        }

        /// <summary>
        /// Gets or sets the parent folder identifier.
        /// </summary>
        public int ParentID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the display order.
        /// </summary>
        public int DisplayOrder
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public ForumFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Construct(IUnitOfWork uow, ForumEntity entity)
        {
            base.Construct(uow, entity);

            entity.Parent = ParentID != 0 
                ? uow.Execute(new GetForum
                {
                    ThrowsException = true,
                    EntityID = ParentID
                }).Result 
                : null;
            entity.DisplayOrder = DisplayOrder;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the forum.
    /// </summary>
    public class UpdateForum : UpdateAuditable<ForumEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FORUM");
            }
        }

        /// <summary>
        /// Gets or sets the parent folder identifier.
        /// </summary>
        public int ParentID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the display order.
        /// </summary>
        public int DisplayOrder
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public ForumFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Revise(IUnitOfWork uow, ForumEntity entity)
        {
            base.Revise(uow, entity);

            entity.Parent = 
                ParentID != 0 ? uow.Execute(new GetForum
                {
                    ThrowsException = true,
                    EntityID = ParentID
                }).Result 
                : null;
            entity.DisplayOrder = DisplayOrder;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the forum statistics.
    /// </summary>
    public class UpdateForumStats : OperateEntityByID<ForumEntity>
    {
        #region Utilities

        protected override void Work(IUnitOfWork uow, out ForumEntity result)
        {
            var forum = uow.Execute(new GetForum
            {
                ThrowsException = true,
                EntityID = EntityID
            }).Result;

            uow.Flush();
            uow.SqlQuery("exec fwk_Forums_UpdateStats @ForumID=:ForumID, @UpdatedOn=:UpdatedOn")
                .SetInt32("ForumID", forum.EntityID)
                .SetDateTime("UpdatedOn", DateTime.UtcNow)
                .ExecuteUpdate();
            uow.Flush();
            
            result = uow.Execute(new RefreshEntity<ForumEntity>
            {
                Entity = forum
            }).Result;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the forum.
    /// </summary>
    public class DeleteForum : DeleteEntity<ForumEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FORUM");
            }
        }

        #endregion

        #region Utilities

        protected override void Destruct(IUnitOfWork uow, ForumEntity entity)
        {
            foreach(var i in entity.Translations)
            {
                uow.Execute(new DeleteForumTranslation
                {
                    EntityID = i.EntityID
                });
            }
            foreach(var i in entity.AccessRules)
            {
                uow.Execute(new DeleteForumAccessRule
                {
                    EntityID = i.EntityID
                });
            }
            base.Destruct(uow, entity);
        }

        #endregion
    }

    /// <summary>
    /// Gets the forum by identifier.
    /// </summary>
    public class GetForum : GetEntity<ForumEntity>,
        IApiOperation
    {
    }

    #endregion

    #region __Translation__

    /// <summary>
    /// Creates a forum translation.
    /// </summary>
    public class CreateForumTranslation : CreateTranslation
        <TForumEntity, ForumEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FORUM");
            }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the description.
        /// </summary>
        public string Description
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Construct(IUnitOfWork uow, TForumEntity entity)
        {
            base.Construct(uow, entity);

            entity.Name = StringHelper.EnsureNotNull(Name).TrimToLen(100);
            entity.Description = StringHelper.EnsureNotNull(Description).TrimToLen(1000);
        }

        #endregion
    }

    /// <summary>
    /// Updates the forum translation.
    /// </summary>
    public class UpdateForumTranslation : UpdateTranslation
        <TForumEntity, ForumEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FORUM");
            }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the description.
        /// </summary>
        public string Description
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Revise(IUnitOfWork uow, TForumEntity entity)
        {
            base.Revise(uow, entity);

            entity.Name = StringHelper.EnsureNotNull(Name).TrimToLen(100);
            entity.Description = StringHelper.EnsureNotNull(Description).TrimToLen(1000);
        }

        #endregion
    }

    /// <summary>
    /// Saves a forum translation.
    /// </summary>
    public class SaveForumTranslation : SaveTranslation
        <TForumEntity, ForumEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FORUM");
            }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the description.
        /// </summary>
        public string Description
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out TForumEntity result)
        {
            var translation = uow.Execute(new GetForumTranslationByLanguage
            {
                HolderID = HolderID,
                LanguageID = LanguageID
            }).Result;
            if(translation == null)
            {
                translation = uow.Execute(new CreateForumTranslation
                {
                    HolderID = HolderID,
                    LanguageID = LanguageID,
                    Name = Name,
                    Description = Description
                }).Result;
            }
            else
            {
                translation = uow.Execute(new UpdateForumTranslation
                {
                    EntityID = translation.EntityID,
                    LanguageID = LanguageID,
                    Name = Name,
                    Description = Description
                }).Result;
            }
            result = translation;
        }

        #endregion
    }


    /// <summary>
    /// Deletes the forum translation.
    /// </summary>
    public class DeleteForumTranslation : DeleteTranlsation
        <TForumEntity, ForumEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FORUM");
            }
        }

        #endregion
    }

    /// <summary>
    /// Gets the forum translation.
    /// </summary>
    public class GetForumTranslation : GetTranlsation
        <TForumEntity, ForumEntity>,
        IApiOperation
    {
    }

    /// <summary>
    /// Gets the forum translation.
    /// </summary>
    public class GetForumTranslationByLanguage : GetTranlsationByLanguage
        <TForumEntity, ForumEntity>,
        IApiOperation
    {
    }

    #endregion

    #region __Search__

    /// <summary>
    /// Search over forum collection.
    /// </summary>
    public class SearchForums : SearchOperation<ForumEntity>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public SearchForums()
        {
            SortDir = SortDirection.Ascending;
            SortBy = new []
            {
                ForumSortBy.ID
            };
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the parent identifier.
        /// </summary>
        public int? ParentID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public ForumFlags FlagMask
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort direction.
        /// </summary>
        public SortDirection SortDir
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort by.
        /// </summary>
        public ForumSortBy[] SortBy
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<ForumEntity> Build(IUnitOfWork uow, IQueryable<ForumEntity> query)
        {
            query = base.Build(uow, query);
            query = query.FilterByFlags(x => (int)x.Flags, (int)FlagMask);
            if(ParentID.HasValue)
                query = query.Where(x => x.ParentID == ParentID.Value);
            if(SortBy != null)
            {
                for(int i = 0; i < SortBy.Length; i++)
                {
                    switch(SortBy[i])
                    {
                        case ForumSortBy.TopicCount:
                            query = query.OrderBy(x => x.TopicCount, SortDir, i != 0);
                            break;
                        case ForumSortBy.PostCount:
                            query = query.OrderBy(x => x.PostCount, SortDir, i != 0);
                            break;
                        case ForumSortBy.DisplayOrder:
                            query = query.OrderBy(x => x.DisplayOrder, SortDir, i != 0);
                            break;
                        case ForumSortBy.CreatedOn:
                            query = query.OrderBy(x => x.CreatedOn, SortDir, i != 0);
                            break;
                        default:
                            query = query.OrderBy(x => x.EntityID, SortDir, i != 0);
                            break;
                    }
                }
            }
            return query;
        }

        #endregion
    }

    #endregion

    #region __ACL__

    /// <summary>
    /// Creates a forum access rule.
    /// </summary>
    public class CreateForumAccessRule : CreateEntity<ForumAccessRuleEntity>
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public CreateForumAccessRule()
        {
            AccessRights = ForumAccessRights.ReadOnly;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the forum identifier.
        /// </summary>
        public int ForumID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the role identifier.
        /// </summary>
        public int RoleID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the access rights.
        /// </summary>
        public ForumAccessRights AccessRights
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Construct(IUnitOfWork uow, ForumAccessRuleEntity entity)
        {
            base.Construct(uow, entity);

            entity.Forum = uow.Execute(new GetForum
            {
                ThrowsException = true,
                EntityID = ForumID
            }).Result;
            entity.Role = uow.Execute(new GetRole
            {
                ThrowsException = true,
                EntityID = RoleID
            }).Result;
            entity.AccessRights = AccessRights;
        }

        #endregion
    }

    /// <summary>
    /// Updates the forum access rule.
    /// </summary>
    public class UpdateForumAccessRule : UpdateEntity<ForumAccessRuleEntity>
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public UpdateForumAccessRule()
        {
            AccessRights = ForumAccessRights.ReadOnly;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the access rights.
        /// </summary>
        public ForumAccessRights AccessRights
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Revise(IUnitOfWork uow, ForumAccessRuleEntity entity)
        {
            base.Revise(uow, entity);

            entity.AccessRights = AccessRights;
        }

        #endregion
    }

    /// <summary>
    /// Sets the forum access rule.
    /// </summary>
    public class SetForumAccessRule : Operation<ForumAccessRuleEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public SetForumAccessRule()
        {
            AccessRights = ForumAccessRights.ReadOnly;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FORUM");
            }
        }

        /// <summary>
        /// Gets or sets the forum identifier.
        /// </summary>
        public int ForumID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the role identifier.
        /// </summary>
        public int RoleID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the access rights.
        /// </summary>
        public ForumAccessRights AccessRights
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out ForumAccessRuleEntity result)
        {
            var entity = uow.Execute(new GetForumAccessRuleByRole
            {
                ForumID = ForumID,
                RoleID = RoleID
            }).Result;

            if(entity == null)
            {
                entity = uow.Execute(new CreateForumAccessRule
                {
                    ForumID = ForumID,
                    RoleID = RoleID,
                    AccessRights = AccessRights
                }).Result;
            }
            else
            {
                entity = uow.Execute(new UpdateForumAccessRule
                {
                    EntityID = entity.EntityID,
                    AccessRights = AccessRights
                }).Result;
            }
            result = entity;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the forum access rule.
    /// </summary>
    public class DeleteForumAccessRule : DeleteEntity<ForumAccessRuleEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FORUM");
            }
        }

        #endregion
    }

    /// <summary>
    /// Gets the forum access rule by identifier.
    /// </summary>
    public class GetForumAccessRule : GetEntity<ForumAccessRuleEntity>,
        IApiOperation
    {
    }

    /// <summary>
    /// Gets the forum accesor rule by code.
    /// </summary>
    public class GetForumAccessRuleByRole : QueryEntity<ForumAccessRuleEntity>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the forum identifier.
        /// </summary>
        public int ForumID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the role identifier.
        /// </summary>
        public int RoleID
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<ForumAccessRuleEntity> Build(IUnitOfWork uow,
            IQueryable<ForumAccessRuleEntity> query)
        {
            return base.Build(uow, query).Where(x =>
                x.Forum.EntityID == ForumID &&
                x.Role.EntityID == RoleID);
        }

        #endregion
    }

    #endregion
}
